package render;

import math.Maths;
import util.Bitmap;
import util.Color;

public class Rasterizer extends RasterizerBase {
    
    public Rasterizer(int imgWidth, int imgHeight, int[] pxs) {
        super(imgWidth, imgHeight, pxs);
    }
    
    public void lineFill(
            int x0, int y0, float z0, 
            int x1, int y1, float z1, int color) 
    {
        if (y0 > y1) { // Ensure v0 is at the top (lowest yval)
            int tx = x1;    x1 = x0;    x0 = tx;
            int ty = y1;    y1 = y0;    y0 = ty;
            float tz = z1;  z1 = z0;    z0 = tz;
        }
        int indPx;
        float z;
        float dy = (y1 - y0);
        float dx = (x1 - x0);
        if (dx > dy || -dx > dy) {
            int y;
            int x = x0;
            boolean condition;
            boolean dxPositive = (dx > 0);
            if (dxPositive) {
                condition = (x < x1);
            } else {
                condition = (x > x1);
            }
            while (condition) {
                z = Maths.lerpf(x, x0, x1, z0, z1);
                y = Maths.lerpi(x, x0, x1, y0, y1);
                indPx = getPixelIndex(x, y);
                if (shouldPixelAt(indPx, x, z)) {
                    pixel(indPx, z, color);
                }
                if (dxPositive) {
                    condition = (x < x1);
                    x++;
                } else {
                    condition = (x > x1);
                    x--;
                }
            }
        } else {
            int x;
            int y = y0;
            while (y < y1) {
                z = Maths.lerpf(y, y0, y1, z0, z1);
                x = Maths.lerpi(y, y0, y1, x0, x1);
                indPx = getPixelIndex(x, y);
                if (shouldPixelAt(indPx, x, z)) {
                    pixel(indPx, z, color);
                }
                y++;
            }
        }
    }
    
    // Works for all orientations of a triangle, and no ordering required!
    public void triFill(
            int x0, int y0, float z0, 
            int x1, int y1, float z1, 
            int x2, int y2, float z2, int color) 
    {
        if (y0 > y1) { // Swap v0, v1
            int tx = x1;    x1 = x0;    x0 = tx;
            int ty = y1;    y1 = y0;    y0 = ty;
            float tz = z1;  z1 = z0;    z0 = tz;
        }
        if (y0 > y2) { // Swap v0, v2
            int tx = x2;    x2 = x0;    x0 = tx;
            int ty = y2;    y2 = y0;    y0 = ty;
            float tz = z2;  z2 = z0;    z0 = tz;
        }
        if (y1 > y2) { // Swap v1, v2
            int tx = x2;    x2 = x1;    x1 = tx;
            int ty = y2;    y2 = y1;    y1 = ty;
            float tz = z2;  z2 = z1;    z1 = tz;
        }
        int xL, xR;
        float zL, zR;
        for (int y = y0; y < y2; y++) {
            xR = Maths.lerpi(y, y0, y2, x0, x2);
            zR = Maths.lerpf(y, y0, y2, z0, z2);
            if (y >= y1) {
                xL = Maths.lerpi(y, y1, y2, x1, x2);
                zL = Maths.lerpf(y, y1, y2, z1, z2);
            } else {
                xL = Maths.lerpi(y, y0, y1, x0, x1);
                zL = Maths.lerpf(y, y0, y1, z0, z1);
            }
            lineHorizFill(xL, xR, y, zL, zR, color);
        }
    }
    
    // Works for all orientations of a triangle, and no ordering required!
    public void triGouraud(
            int x0, int y0, float z0, 
            int x1, int y1, float z1, 
            int x2, int y2, float z2,
            int col0, int col1, int col2)
    {
        if (y0 > y1) { // Swap v0, v1
            int tx = x1;    x1 = x0;    x0 = tx;
            int ty = y1;    y1 = y0;    y0 = ty;
            float tz = z1;  z1 = z0;    z0 = tz;
            int tcol = col1;    col1 = col0;    col0 = tcol;
        }
        if (y0 > y2) { // Swap v0, v2
            int tx = x2;    x2 = x0;    x0 = tx;
            int ty = y2;    y2 = y0;    y0 = ty;
            float tz = z2;  z2 = z0;    z0 = tz;
            int tcol = col2;    col2 = col0;    col0 = tcol;
        }
        if (y1 > y2) { // Swap v1, v2
            int tx = x2;    x2 = x1;    x1 = tx;
            int ty = y2;    y2 = y1;    y1 = ty;
            float tz = z2;  z2 = z1;    z1 = tz;
            int tcol = col2;    col2 = col1;    col1 = tcol;
        }
        int a0 = col0 >> 24 & 0xFF;
        int r0 = col0 >> 16 & 0xFF;
        int g0 = col0 >> 8 & 0xFF;
        int b0 = col0 & 0xFF;
        int a1 = col1 >> 24 & 0xFF;
        int r1 = col1 >> 16 & 0xFF;
        int g1 = col1 >> 8 & 0xFF;
        int b1 = col1 & 0xFF;
        int a2 = col2 >> 24 & 0xFF;
        int r2 = col2 >> 16 & 0xFF;
        int g2 = col2 >> 8 & 0xFF;
        int b2 = col2 & 0xFF;
        int xL, xR;
        float zL, zR;
        int colL, colR;
        int aL, rL, gL, bL;
        int aR, rR, gR, bR;
        for (int y = y0; y < y2; y++) {
            xR = Maths.lerpi(y, y0, y2, x0, x2);
            zR = Maths.lerpf(y, y0, y2, z0, z2);
            aR = Maths.lerpi(y, y0, y2, a0, a2);
            rR = Maths.lerpi(y, y0, y2, r0, r2);
            gR = Maths.lerpi(y, y0, y2, g0, g2);
            bR = Maths.lerpi(y, y0, y2, b0, b2);
            if (y >= y1) {
                xL = Maths.lerpi(y, y1, y2, x1, x2);
                zL = Maths.lerpf(y, y1, y2, z1, z2);
                aL = Maths.lerpi(y, y1, y2, a1, a2);
                rL = Maths.lerpi(y, y1, y2, r1, r2);
                gL = Maths.lerpi(y, y1, y2, g1, g2);
                bL = Maths.lerpi(y, y1, y2, b1, b2);
            } else {
                xL = Maths.lerpi(y, y0, y1, x0, x1);
                zL = Maths.lerpf(y, y0, y1, z0, z1);
                aL = Maths.lerpi(y, y0, y1, a0, a1);
                rL = Maths.lerpi(y, y0, y1, r0, r1);
                gL = Maths.lerpi(y, y0, y1, g0, g1);
                bL = Maths.lerpi(y, y0, y1, b0, b1);
            }
            colR = Color.getColorRGBAi(rR, gR, bR, aR);
            colL = Color.getColorRGBAi(rL, gL, bL, aL);
            lineHorizGouraud(xL, xR, y, zL, zR, colL, colR);
        }
    }
    
    // Works for all orientations of a triangle, and no ordering required!
    //    Note: the middle t value is not really used
    public void triBitmap(
            int x0, int y0, float z0, float s0, float t0,
            int x1, int y1, float z1, float s1, float t1,
            int x2, int y2, float z2, float s2, float t2, Bitmap bmp) 
    {
        if (y0 > y1) { // Swap v0, v1
            int tx = x1;    x1 = x0;    x0 = tx;
            int ty = y1;    y1 = y0;    y0 = ty;
            float tz = z1;    z1 = z0;    z0 = tz;
            float ts = s1;    s1 = s0;    s0 = ts;
            float tt = t1;    t1 = t0;    t0 = tt;
        }
        if (y0 > y2) { // Swap v0, v2
            int tx = x2;    x2 = x0;    x0 = tx;
            int ty = y2;    y2 = y0;    y0 = ty;
            float tz = z2;    z2 = z0;    z0 = tz;
            float ts = s2;    s2 = s0;    s0 = ts;
            float tt = t2;    t2 = t0;    t0 = tt;
        }
        if (y1 > y2) { // Swap v1, v2
            int tx = x2;    x2 = x1;    x1 = tx;
            int ty = y2;    y2 = y1;    y1 = ty;
            float tz = z2;    z2 = z1;    z1 = tz;
            float ts = s2;    s2 = s1;    s1 = ts;
            float tt = t2;    t2 = t1;    t1 = tt;
        }
        int xL, xR;
        float zL, zR;
        float sL, sR;
        float tL, tR;
        for (int y = y0; y < y2; y++) {
            xR = Maths.lerpi(y, y0, y2, x0, x2);
            zR = Maths.lerpf(y, y0, y2, z0, z2);
            sR = Maths.lerpf(y, y0, y2, s0, s2);
            tR = Maths.lerpf(y, y0, y2, t0, t2);
            if (y >= y1) {
                xL = Maths.lerpi(y, y1, y2, x1, x2);
                zL = Maths.lerpf(y, y1, y2, z1, z2);
                sL = Maths.lerpf(y, y1, y2, s1, s2);
                tL = Maths.lerpf(y, y1, y2, t1, t2);
            } else {
                xL = Maths.lerpi(y, y0, y1, x0, x1);
                zL = Maths.lerpf(y, y0, y1, z0, z1);
                sL = Maths.lerpf(y, y0, y1, s0, s1);
                tL = Maths.lerpf(y, y0, y1, t0, t1);
            }
            lineHorizTex(xL, xR, y, zL, zR, sL, sR, tL, tR, bmp);
        }
    }
    
    public void triBitmapf(
            float x0, float y0, float z0, float s0, float t0,
            float x1, float y1, float z1, float s1, float t1,
            float x2, float y2, float z2, float s2, float t2, Bitmap bmp) 
    {
        if (y0 > y1) { // Swap v0, v1
            float tx = x1;    x1 = x0;    x0 = tx;
            float ty = y1;    y1 = y0;    y0 = ty;
            float tz = z1;    z1 = z0;    z0 = tz;
            float ts = s1;    s1 = s0;    s0 = ts;
            float tt = t1;    t1 = t0;    t0 = tt;
        }
        if (y0 > y2) { // Swap v0, v2
            float tx = x2;    x2 = x0;    x0 = tx;
            float ty = y2;    y2 = y0;    y0 = ty;
            float tz = z2;    z2 = z0;    z0 = tz;
            float ts = s2;    s2 = s0;    s0 = ts;
            float tt = t2;    t2 = t0;    t0 = tt;
        }
        if (y1 > y2) { // Swap v1, v2
            float tx = x2;    x2 = x1;    x1 = tx;
            float ty = y2;    y2 = y1;    y1 = ty;
            float tz = z2;    z2 = z1;    z1 = tz;
            float ts = s2;    s2 = s1;    s1 = ts;
            float tt = t2;    t2 = t1;    t1 = tt;
        }
        float iz0 = 1.0f / z0;
        float iz1 = 1.0f / z1;
        float iz2 = 1.0f / z2;
        
        float xL, xR;
        float izL, izR;
        float sizL, sizR;
        float tizL, tizR;
        int yint;
        for (float y = y0; y < y2; y++) {
            yint = (int)y;
            xR = Maths.lerpf(y, y0, y2, x0, x2);
            izR = Maths.lerpf(y, y0, y2, iz0, iz2);
            sizR = Maths.lerpf(y, y0, y2, s0*izR, s2*izR);
            tizR = Maths.lerpf(y, y0, y2, t0*izR, t2*izR);
            if (y >= y1) {
                xL = Maths.lerpf(y, y1, y2, x1, x2);
                izL = Maths.lerpf(y, y1, y2, iz1, iz2);
                sizL = Maths.lerpf(y, y1, y2, s1*izL, s2*izL);
                tizL = Maths.lerpf(y, y1, y2, t1*izL, t2*izL);
            } else {
                xL = Maths.lerpf(y, y0, y1, x0, x1);
                izL = Maths.lerpf(y, y0, y1, iz0, iz1);
                sizL = Maths.lerpf(y, y0, y1, s0*izL, s1*izL);
                tizL = Maths.lerpf(y, y0, y1, t0*izL, t1*izL);
            }
            lineHorizTexf(xL, xR, yint, izL, izR, sizL, sizR, tizL, tizR, bmp);
        }
    }
    
    public void lineHorizTexf(
            float xL, float xR, 
            int y, 
            float izL, float izR, 
            float sizL, float sizR, 
            float tizL, float tizR, Bitmap bmp)
    {
        if (xL > xR) {
            float tx = xR;      xR = xL;        xL = tx;
            float tiz = izR;    izR = izL;      izL = tiz;
            float ts = sizR;    sizR = sizL;    sizL = ts;
            float tt = tizR;    tizR = tizL;    tizL = tt;
        }
        float zL = 1.0f / izL;
        float zR = 1.0f / izR;
        float sL = zL * sizL;
        float sR = zR * sizR;
        float tL = zL * tizL;
        float tR = zR * tizR;
        
        int texelX;
        int texelY;
        int indPx;
        int xint;
        float iz;
        float siz, tiz;
        float s, t;
        for (float x = xL; x < xR; x++) {
            xint = (int)x;
            iz = Maths.lerpf(x, xL, xR, izL, izR);
            indPx = getPixelIndex(xint, y);
            if (shouldPixelAt(indPx, xint, iz)) {
                siz = Maths.lerpf(x, xL, xR, sL * iz, sR * iz);
                tiz = Maths.lerpf(x, xL, xR, tL * iz, tR * iz);
                s = siz / iz;
                t = tiz / iz;
                texelX = Maths.capi((int)(s * bmp.wi), 0, bmp.wi);
                texelY = Maths.capi((int)(t * bmp.hi), 0, bmp.hi);
                pixel(indPx, 1.0f/iz, bmp.pixels[texelX + bmp.w * texelY]);
            }
        }
    }
    
    
    public void lineHorizFill(
            int x0, int x1, 
            int y, 
            float z0, float z1, int color) 
    {
        if (x0 > x1) {
            int tx = x1;    x1 = x0;    x0 = tx;
            float tz = z1;  z1 = z0;    z0 = tz;
        }
        float z;
        int indPx;
        for (int x = x0; x < x1; x++) {
            z = Maths.lerpf(x, x0, x1, z0, z1);
            indPx = getPixelIndex(x, y);
            if (shouldPixelAt(indPx, x, z)) {
                pixel(indPx, z, color);
            }
        }
    }
    
    public void lineHorizGouraud(
            int x0, int x1, 
            int y, 
            float z0, float z1, 
            int col0, int col1) 
    {
        if (x0 > x1) {
            int tx = x1;    x1 = x0;    x0 = tx;
            float tz = z1;  z1 = z0;    z0 = tz;
            int tcol = col1;    col1 = col0;    col0 = tcol;
        }
        int a0 = col0 >> 24 & 0xFF;
        int r0 = col0 >> 16 & 0xFF;
        int g0 = col0 >> 8 & 0xFF;
        int b0 = col0 & 0xFF;
        int a1 = col1 >> 24 & 0xFF;
        int r1 = col1 >> 16 & 0xFF;
        int g1 = col1 >> 8 & 0xFF;
        int b1 = col1 & 0xFF;
        int a, r, g, b;
        float z;
        int indPx;
        for (int x = x0; x < x1; x++) {
            z = Maths.lerpf(x, x0, x1, z0, z1);
            indPx = getPixelIndex(x, y);
            if (shouldPixelAt(indPx, x, z)) {
                a = Maths.lerpi(x, x0, x1, a0, a1);
                r = Maths.lerpi(x, x0, x1, r0, r1);
                g = Maths.lerpi(x, x0, x1, g0, g1);
                b = Maths.lerpi(x, x0, x1, b0, b1);
                pixel(indPx, z, Color.getColorRGBAi(r, g, b, a));
            }
        }
    }
    
    public void lineHorizTex(
            int x0, int x1, 
            int y, 
            float iz0, float iz1, 
            float s0, float s1, 
            float t0, float t1, Bitmap bmp)
    {
        if (x0 > x1) {
            int tx = x1;    x1 = x0;    x0 = tx;
            float tiz = iz1;  iz1 = iz0;    iz0 = tiz;
            float ts = s1;  s1 = s0;    s0 = ts;
            float tt = t1;  t1 = t0;    t0 = tt;
        }
        int xsrc;
        int ysrc;
        float iz;
        float z;
        int indPx;
        for (int x = x0; x < x1; x++) {
            iz = Maths.lerpf(x, x0, x1, iz0, iz1);
            z = 1f / iz;
            indPx = getPixelIndex(x, y);
            if (shouldPixelAt(indPx, x, z)) {
                ysrc = (int)(bmp.hi * Maths.lerpf(x, x0, x1, t0 * iz0, t1 * iz1) / iz);
                xsrc = (int)(bmp.wi * Maths.lerpf(x, x0, x1, s0 * iz0, s1 * iz1) / iz);
                pixel(indPx, iz, bmp.pixels[xsrc + bmp.w * ysrc]);
            }
        }
    }
    
}

/*


//    // Tests at least 2x the necessary pixels, inefficient
//    public void triFillBBox(vec3f v0, vec3f v1, vec3f v2, int color) {
//        int xmin = Maths.min(v0.x, v1.x, v2.x);
//        int xmax = Maths.max(v0.x, v1.x, v2.x);
//        int ymin = Maths.min(v0.y, v1.y, v2.y);
//        int ymax = Maths.max(v0.y, v1.y, v2.y);
//        int v0.xm1 = v0.x - v1.x;
//        int v1.xm2 = v1.x - v2.x;
//        int v2.xm0 = v2.x - v0.x;
//        int v0.ym1 = v0.y - v1.y;
//        int v1.ym2 = v1.y - v2.y;
//        int v2.ym0 = v2.y - v0.y;
//        for (int y = ymin; y < ymax; y++) {
//            for (int x = xmin; x < xmax; x++) {
//                if ((v0.xm1 * (y - v0.y) - v0.ym1 * (x - v0.x) > 0)
//                        || (v1.xm2 * (y - v1.y) - v1.ym2 * (x - v1.x) > 0)
//                        || (v2.xm0 * (y - v2.y) - v2.ym0 * (x - v2.x) > 0)); else {
//                    pixels[(x + y * drawW)] = color;
//                }
//            }
//        }
//    }
    

    public void lineHorizTex(int x0, int x1, int y, float z0, float z1, float s0, float s1, float t, Bitmap bmp) {
        if (x0 > x1) {
            int tx = x1;    x1 = x0;    x0 = tx;
            float tz = z1;  z1 = z0;    z0 = tz;
            float ts = s1;  s1 = s0;    s0 = ts;
        }
        if (z0 == 0f) {
            z0 = 1E-6f;
        }
        if (z1 == 0f) {
            z1 = 1E-6f;
        }
        int xsrc;
        int ysrc = (int)Maths.lerp(t, 0, bmp.hi);
        float smult0 = s0 * bmp.wi;
        float smult1 = s1 * bmp.wi;
        //        float rec_z0 = 1.0f / z0;
        //        float rec_z1 = 1.0f / z1;
        float z;
        int indPx;
        for (int x = x0; x < x1; x++) {
            z = Maths.lerpf(x, x0, x1, z0, z1);
            indPx = getPixelIndex(x, y);
            if (shouldPixelAt(indPx, x, z)) {
                xsrc = Maths.lerpi(x, x0, x1, smult0, smult1);
                //                ysrc = (int)Maths.lerp(t, 0, bmp.hi);
                //                xsrc = (int)(Maths.lerpf(x, x0, x1, smult0 * rec_z0, smult1 * rec_z1) / Maths.lerpf(x, x0, x1, rec_z0, rec_z1));
                if (xsrc < 0 || xsrc >= bmp.w) {
                    continue;
                }
                pixel(indPx, z, bmp.pixels[xsrc + bmp.w * ysrc]);
            }
        }
    }


    //    public void rectBitmap(int x0, int y0, int x1, int y1, float s0, float t0, float s1, float t1, Bitmap bmp) {
    //        float tmult0 = t0 * bmp.hi;
    //        float tmult1 = t1 * bmp.hi;
    //        float smult0 = s0 * bmp.wi;
    //        float smult1 = s1 * bmp.wi;
    //        int x, y;
    //        int xsrc, ysrc;
    //        for (y = y0; y < y1; y++) {
    //            ysrc = Maths.lerpi(y, y0, y1, tmult0, tmult1);
    //            if (ysrc < 0 || ysrc >= bmp.h) {
    //                continue;
    //            }
    //            for (x = x0; x < x1; x++) {
    //                xsrc = Maths.lerpi(x, x0, x1, smult0, smult1);
    //                if (xsrc < 0 || xsrc >= bmp.w) {
    //                    continue;
    //                }
    //                pixel(x, y, bmp.pixels[xsrc + bmp.w * ysrc]);
    //            }
    //        }
    //    }
    
    //    public void rectOutline(int x0, int y0, float z0, int x1, int y1, float z1, int color) {
    //        lineFill(x0, y0, z0, x0, y1, color);
    //        lineFill(x0, y0, x1, y0, color);
    //        lineFill(x0, y1, x1, y1, color);
    //        lineFill(x1, y0, x1, y1, color);
    //    }
    
    //    public void rectFill(int x0, int y0, int x1, int y1, int col) {
    //        int x, y;
    //        for (y = y0; y < y1; y++) {
    //            for (x = x0; x < x1; x++) {
    //                pixel(x, y, col);
    //            }
    //        }
    //    }


*/